import wsgiref.handlers
import json
from google.appengine.ext import db
from google.appengine.ext import webapp
from datetime import date

# class representing a track entity
class Track(db.Model):
    userID = db.StringProperty()
    playlist = db.StringProperty()
    title = db.StringProperty()
    artist = db.StringProperty()
    id = db.StringProperty()
    duration = db.StringProperty()
    thumbnail = db.StringProperty()
    rating = db.StringProperty()

# class representing a playlist entity
class Playlist(db.Model):
    userID = db.StringProperty()
    name = db.StringProperty()

# class representing a TrackTransaction entity, meaning a track 
# sent from one use to another
class TrackTransaction(db.Model):
    senderID = db.StringProperty()
    receiverID = db.StringProperty()
    message = db.StringProperty()
    when = db.DateTimeProperty(auto_now_add = True)
    title = db.StringProperty()
    artist = db.StringProperty()
    id = db.StringProperty()
    duration = db.StringProperty()
    thumbnail = db.StringProperty()
    read = db.StringProperty()

class iPlayerHandler(webapp.RequestHandler):
    def get(self):
        action  = self.request.get("action")
        if(action == "getTracks"):
            self._handleGetTracks()
        elif(action == "getPlaylists"):
            self._handleGetPlaylists()
        elif(action == "getTopTracks"):
            self._getTopTracks()
        else:
            self._returnTrackTransactions()
    
    # gets user's tracks from data store
    def _handleGetTracks(self):
        tracks = []
        userID = self.request.get("userID")
        playlist = self.request.get("playlist")
        q = Track.gql('WHERE userID=:userID AND playlist=:playlist',userID=userID, playlist=playlist)
        for track in q:
          item = { 'playlist' : track.playlist,
                   'title' : track.title,
                   'artist' : track.artist,
                   'id' : track.id,
                   'duration' : track.duration,
                   'thumbnail' : track.thumbnail,
                   'rating' : track.rating,
                   'key' : str(track.key()) }
          tracks.append(item)
        self.response.out.write(json.write(tracks))
    
    # gets user's playlist from data store
    def _handleGetPlaylists(self):
        playlists = []
        userID = self.request.get("userID")
        q = Playlist.gql('WHERE userID=:userID',userID=userID)
        for playlist in q:
          item = { 'name' : playlist.name,
                   'key' : str(playlist.key()) }
          playlists.append(item)
        self.response.out.write(json.write(playlists))
    
    # gets user's track transactions from data store
    def _returnTrackTransactions(self):
        senderID = self.request.get("senderID")
        receiverID = self.request.get("receiverID")
        trackTransactions = self._getTrackTransactions(senderID, receiverID)
        
        results = []
        for trackTransaction in trackTransactions:
            item = { 'senderID' : trackTransaction.senderID,
                     'receiverID' : trackTransaction.receiverID,
                     'message' : trackTransaction.message,
                     'when' : str(trackTransaction.when),
                     'track_title' : trackTransaction.title,
                     'track_artist' : trackTransaction.artist,
                     'track_id' : trackTransaction.id,
                     'track_duration' : trackTransaction.duration,
                     'track_thumbnail' : trackTransaction.thumbnail,
                     'read' : trackTransaction.read,
                     'key' : str(trackTransaction.key()) }
            results.append(item)
        self.response.out.write(json.write(results))
    
    def _getTrackTransactions(self, senderID, receiverID):
        results = []
        if senderID:
            results = TrackTransaction.gql('WHERE senderID=:senderID ORDER BY when DESC', senderID=senderID)
        
        elif receiverID:
            results = TrackTransaction.gql('WHERE receiverID=:receiverID ORDER BY when DESC', receiverID=receiverID)
        
        else:
            result = TrackTransaction.all()
        
        return results;
    
    # gets the top 20 tracks
    def _getTopTracks(self):
        results = []
        type = self.request.get("type")
        if(type == "friends"):
            query_string = "WHERE userID IN ("
            n = int(self.request.get("n"))
            for i in range(n):
                query_string = query_string + "'" + self.request.get("userID" + str(i)) + "'"
                if(i < n-1):
                    query_string = query_string + ","
                else:
                    query_string = query_string + ")"
            query_string = query_string + " ORDER BY id"
            
            tracks = Track.gql(query_string)
        else:
            tracks = Track.all()
            tracks.order('id')
        
        for track in tracks:
          item = { 'playlist' : track.playlist,
                   'title' : track.title,
                   'artist' : track.artist,
                   'id' : track.id,
                   'duration' : track.duration,
                   'thumbnail' : track.thumbnail,
                   'rating' : track.rating,
                   'key' : str(track.key()) }
          results.append(item)
        self.response.out.write(json.write(results))
             
    # calculates average ratings by using Weighted Bayesian Rating System 
    def _calcAvgRating(self, tracks):
        results = []
        currTrack = Track()
        currTrack.id = ""
        # Parameters for calculating the Bayesian average
        ratingSum = 0
        ratingAvg = 0
        n = 0
        W = 0
        m = 0
        C = 0
        ttlSum = 0
        counter = 0
        numOfSongs = 0
        for track in tracks:
            counter = counter + 1
            if(track.id == currTrack.id):
                ratingSum = ratingSum + int(track.rating)
                n = n+1
            # Finished going over a certain track
            else:
                numOfSongs = numOfSongs + 1
                if(n != 0):
                    ratingAvg = int(ratingSum)/n
                    item = [ currTrack.title,
                             currTrack.artist,
                             currTrack.id,
                             currTrack.duration,
                             currTrack.thumbnail,
                             ratingAvg, 
                             n ]
                    results.append(item)         
                m = m + n
                m = float(m) / float(numOfSongs)
                currTrack = track
                ttlSum += ratingSum
                ratingSum = int(track.rating)
                n = 1
       
        if(n != 0):
            ratingAvg = ratingSum/n
            item = [ currTrack.title,
                     currTrack.artist,
                     currTrack.id,
                     currTrack.duration,
                     currTrack.thumbnail,
                     ratingAvg,
                     n ]
            results.append(item)
            m = m + n
            ttlSum += ratingSum
            C = ttlSum / counter
        for i in range(len(results)):
            #Calculate weighted Bayesian average
            W = (float(results[i][6]) / float(results[i][6] + m))*float(results[i][5]) + (float(m) / float(results[i][6] + m))*float(C)
            results[i][5] = W
        results.sort(lambda a,b: cmp(a[5], b[5]))
        results.reverse()
        return results;
       
    def post(self):
        action = self.request.get('action')
        if(action == "delete"):
            self._handleDelete()
        elif (action == "add"):
            self._handleAdd()
        elif (action == "update"):
            self._handleUpdate()
        elif (action == "addPlaylist"):
            self._handleAddPlaylist()
        elif (action == "deleteTrackTransaction"):
            self._handleDeleteTrackTransaction()
        elif (action == "deletePlaylist"):
            self._handleDeletePlaylist()
        elif (action == "renamePlaylist"):
            self._handleRenamePlaylist()
        elif (action == "markReadTrackTransaction"):
            self._handleMarkReadTransaction()
        else:
            self._handleAddTrackTransaction()
    
    # adds a playlist to the data store
    def _handleAddPlaylist(self):
        playlist = Playlist()
        playlist.userID = self.request.get('userID')
        playlist.name = self.request.get('name')
        playlist.put()
    
    # adds a track to the data store     
    def _handleAdd(self):
        track = Track()
        track.userID = self.request.get('userID')
        track.playlist = self.request.get('playlist')
        track.title = self.request.get('title')
        track.artist = self.request.get('artist')
        track.id = self.request.get('id')
        track.duration = self.request.get('duration')
        track.thumbnail = self.request.get('thumbnail')
        track.rating = self.request.get('rating')
        track.put()
    
    # deletes a track from the data store    
    def _handleDelete(self):
        track = Track.get(self.request.get('key'))
        db.delete(track)
    
    # deletes a playlist from the data store
    def _handleDeletePlaylist(self):
        userID = self.request.get('userID')
        playlist = self.request.get('playlist')
        q = Track.gql('WHERE userID=:userID AND playlist=:playlist',userID=userID, playlist=playlist)
        for track in q:
            track.delete()
        results = Playlist.gql('WHERE userID=:userID AND name=:playlist',userID=userID, playlist=playlist)
        for result in results:
            result.delete()
    
    # deletes a track transaction from the data store    
    def _handleDeleteTrackTransaction(self):
        trackTransaction = TrackTransaction.get(self.request.get('key'))
        db.delete(trackTransaction)
    
    # updates a track from the data store    
    def _handleUpdate(self):
        track = Track.get(self.request.get('key'))
        track.userID = self.request.get('userID')
        track.playlist = self.request.get('playlist')
        track.title = self.request.get('title')
        track.artist = self.request.get('artist')
        track.id = self.request.get('id')
        track.duration = self.request.get('duration')
        track.thumbnail = self.request.get('thumbnail')
        track.rating = self.request.get('rating')
        track.put()
    
    # adds a track transaction to the data store    
    def _handleAddTrackTransaction(self):
        trackTransaction = TrackTransaction()
        trackTransaction.senderID = self.request.get('senderID')
        trackTransaction.receiverID = self.request.get('receiverID')
        trackTransaction.message = self.request.get('message')
        track = Track.get(self.request.get('track_key'))
        trackTransaction.title = track.title
        trackTransaction.artist = track.artist
        trackTransaction.id = track.id
        trackTransaction.duration = track.duration
        trackTransaction.thumbnail = track.thumbnail
        trackTransaction.read = self.request.get('read')
        trackTransaction.put()
    
    # renames a playlist
    def _handleRenamePlaylist(self):
        userID = self.request.get('userID')
        prevName = self.request.get('prevName')
        newName = self.request.get('newName')
        results = Playlist.gql('WHERE userID=:userID AND name=:prevName',userID=userID, prevName=prevName)
        db.delete(results)
        playlist = Playlist()
        playlist.userID = self.request.get('userID')
        playlist.name = newName
        playlist.put()
        q = Track.gql('WHERE userID=:userID AND playlist=:prevName',userID=userID, prevName=prevName)
        for track in q:
            track.playlist = newName
            track.put()
    
    # marks a track transaction as read, meaning a user has viewed this transaction
    def _handleMarkReadTransaction(self):
         trackTransaction = TrackTransaction.get(self.request.get('key'))
         trackTransaction.read = "read"
         trackTransaction.put()

def main():
    app = webapp.WSGIApplication([(r'.*', iPlayerHandler)], debug=True)
    wsgiref.handlers.CGIHandler().run(app)

if __name__ == "__main__":
    main()